import utils from "./utils.js";

export default class Request {
    constructor() {
        this.config = {
            baseUrl: '', // 请求的根域名
            header: {
                'content-type': 'application/x-www-form-urlencoded;charset=UTF-8'
            }, // 默认的请求头
            timeout: 10000, // 超时时间
            method: 'POST', // 请求方式
            dataType: 'json', // 设置为json，返回后uni.request会对数据进行一次JSON.parse
            responseType: 'text', // 此参数无需处理，因为5+和支付宝小程序不支持，默认为text即可
            showLoading: true, // 是否显示请求中的loading
            loadingText: '加载中...', // loading文字
            loadingTime: 1000, // 在此时间内，请求还没回来的话，就显示加载中动画，单位ms
            timer: null, // 定时器
            originalData: false, // 是否在拦截器中返回服务端的原始数据，见文档说明
            loadingMask: true, // 展示loading的时候，是否给一个透明的蒙层，防止触摸穿透
            handleError: true, // 是否处理错误
            requestIntercept: null, // 请求拦截器
            responseIntercept: null, // 响应拦截器
        }
        // get请求
        this.get = (url = "", data = {}, options = {}) => {
            return this.request({
                url,
                data,
                method: 'GET',
                ...options
            });
        }

        // post请求
        this.post = (url = "", data = {}, options = {}) => {
            return this.request({
                url,
                data,
                method: 'POST',
                ...options,
            })
        }

        // json请求
        this.json = (url = "", data = {}, options = {}) => {
            return this.request({
                url,
                data,
                method: 'POST',
                header: {
                    'content-type': 'application/json'
                },
                ...options,
            })
        }

        // put请求，不支持支付宝小程序(HX2.6.15)
        this.put = (url = "", data = {}, options = {}) => {
            return this.request({
                url,
                data,
                method: 'PUT',
                ...options,
            })
        }

        // delete请求，不支持支付宝和头条小程序(HX2.6.15)
        this.delete = (url = "", data = {}, options = {}) => {
            return this.request({
                url,
                data,
                method: 'DELETE',
                ...options,
            })
        }

        // #ifdef H5
        // jsonp请求
        this.jsonp = (url = "", data = {}) => {
            //参数解析  URL为访问的接口 opt为传播的数据  option 为接受参数的回调函数
            return new Promise((resolve, reject) => {
                url = url + utils.encodeParams(data);
                let script = document.createElement("script");
                script.src = url;
                script.type = "text/javascript";
                window[data.callback] = (res) => {
                    delete window[data.callback];
                    document.head.removeChild(script)
                    //接受成功后调用resolve
                    if (res) {
                        resolve(res)
                    } else {
                        reject("服务器暂没有获取到数据")
                    }
                }
                script.addEventListener('error', () => {
                    delete window[data.callback];
                    document.head.removeChild(script);
                    reject('服务器加载失败！');
                });
                document.head.appendChild(script);
            })
        }
        // #endif
    };



    // 设置全局默认配置
    setConfig(customConfig) {
        // 深度合并对象，否则会造成对象深层属性丢失
        this.config = utils.deepMerge(this.config, customConfig);
    }
    // 获取全局配置
    getConfig() {
        return this.config;
    }

    // 主要请求部分
    request(options = {}) {
        // 判断用户传递的URL是否/开头,如果不是,加上/
        const urlRegExp = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w-.\/?%&=]*)?/;
        options.url = urlRegExp.test(options.url) ? options.url : (this.config.baseUrl + (options.url
            .indexOf(
                '/') == 0 ?
            options.url : '/' + options.url));
        options.data = options.data || {};
        options.method = options.method || this.config.method;
        options.timeout = options.timeout || this.config.timeout;
        options.dataType = options.dataType || this.config.dataType;
        options.header = Object.assign({}, this.config.header, options.header);
        options.responseType = options.responseType || this.config.responseType;
        options.handleError = options.handleError === false ? options.handleError : this.config.handleError;

        // 检查请求拦截
        if (this.config.requestIntercept && typeof this.config.requestIntercept === 'function') {
            // 获取请求拦截返回信息
            let interceptorRequest = this.config.requestIntercept(options);
            // 这里判断请求拦截的返回信息，返回一个处于pending状态中的Promise，来取消原promise，避免进入then()回调
            if (interceptorRequest === false) return new Promise(() => {});
            // 覆盖请求配置
            this.options = interceptorRequest;
        }

        return new Promise((resolve, reject) => {
            // 接口调用结束的回调函数（调用成功、失败都会执行）
            options.complete = (response) => {
                // 请求返回后，隐藏loading(如果请求返回快的话，可能会没有loading)
                uni.hideLoading();
                // 清除定时器，如果请求回来了，就无需loading
                clearTimeout(this.config.timer);
                this.config.timer = null;
                // 判断用户对拦截返回数据的要求，如果originalData为true，返回所有的数据(response)到拦截器，否则只返回response.data
                if (this.config.originalData) {
                    // 检查响应拦截器
                    if (this.config.responseIntercept && typeof this.config.responseIntercept ===
                        'function') {
                        // 获取响应拦截返回信息
                        let interceptorResponse = this.config.responseIntercept(response, options);
                        // 如果响应拦截器返回信息，就进入then回调
                        if (interceptorResponse) {
                            resolve(interceptorResponse);
                        } else {
                            // 反之直接进入catch回调
                            reject(response);
                        }
                    } else {
                        // 如果要求返回原始数据，就算没有拦截器，也返回最原始的数据
                        resolve(response);
                    }
                } else {
                    // 接口响应成功
                    if (response.statusCode == 200) {
                        // 检查响应拦截器
                        if (this.config.responseIntercept && typeof this.config.responseIntercept ===
                            'function') {
                            // 获取响应拦截返回信息
                            let interceptorResponse = this.config.responseIntercept(response.data,
                                options);
                            // 如果响应拦截器返回信息，就进入then回调
                            if (interceptorResponse) {
                                resolve(interceptorResponse.data);
                            } else {
                                // 反之直接进入catch回调
                                reject(response.data);
                            }
                        } else {
                            // 没有响应拦截器直接返回
                            resolve(response.data);
                        }
                    } else {
                        // 不返回原始数据的情况下，服务器状态码不为200，modal弹框提示
                        if (response.errMsg) {
                            uni.showModal({
                                title: '请求失败',
                                content: `status:${response.statusCode} \n error:${response.data.message}`,
                                showCancel: false
                            });
                        }
                        reject(response);
                    }
                }
            }

            // 是否显示loading
            // 加一个是否已有timer定时器的判断，否则有两个同时请求的时候，后者会清除前者的定时器id
            // 而没有清除前者的定时器，导致前者超时，一直显示loading
            if (this.config.showLoading && !this.config.timer) {
                this.config.timer = setTimeout(() => {
                    uni.showLoading({
                        title: this.config.loadingText,
                        mask: this.config.loadingMask
                    })
                    this.config.timer = null;
                }, this.config.loadingTime);
            }
            uni.request(options);
        })
    }
}
